--- Handles branch data hazards, and ALSO jump hazards for JAR and JARL

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity branch_hazard_unit is
	
	PORT(
		
	ex_mem_is_reg_write : in STD_LOGIC;
	ex_mem_reg_write_index : in STD_LOGIC_VECTOR(4 downto 0);
	reg1_read_addr : in STD_LOGIC_VECTOR(4 downto 0);
	reg2_read_addr : in STD_LOGIC_VECTOR(4 downto 0);
	ex_mem_reg_data : in STD_LOGIC_VECTOR(31 downto 0);
	
	id_ex_is_reg_write : in STD_LOGIC;
	id_ex_reg_write_index : in STD_LOGIC_VECTOR(4 downto 0);
	ex_mem_is_mem_read : in STD_LOGIC;
	ex_mem_mem_to_reg : in STD_LOGIC;
	
	reg_data_out1 : in STD_LOGIC_VECTOR(31 downto 0);
	reg_data_out2 : in STD_LOGIC_VECTOR(31 downto 0);
	
	-- 00 for non-branch, 01 for beq, 10 for bgez and bgezal, 11 for JAR (just for stalling, comparison is always successful)
	comparison_type : in STD_LOGIC_VECTOR(1 downto 0);
	
	is_branch : out STD_LOGIC;
	is_branch_stall : out STD_LOGIC
	
	);

end branch_hazard_unit;

architecture Behavioral of branch_hazard_unit is

begin

	process(	reg_data_out1,
				reg_data_out2,
				ex_mem_is_reg_write,
				ex_mem_reg_write_index,
				reg1_read_addr,
				ex_mem_reg_data,
				reg2_read_addr,
				comparison_type,
				id_ex_is_reg_write,
				id_ex_reg_write_index,
				ex_mem_is_mem_read,
				ex_mem_mem_to_reg)
		variable real_reg1_branch_operand : STD_LOGIC_VECTOR(31 downto 0); -- for rs
		variable real_reg2_branch_operand : STD_LOGIC_VECTOR(31 downto 0); -- for rt
		variable is_branch_successful_comp : STD_LOGIC;
	
		variable need_to_stall : STD_LOGIC; -- Do we need to stall because one of the comparands is dependant on a previous instruction (LW, R-TYPE, ETC)
		variable should_branch : STD_LOGIC;
	begin
		
		-- By default, we take the operands for comparison from the register file
		real_reg1_branch_operand := reg_data_out1;
		real_reg2_branch_operand := reg_data_out2;
		
		need_to_stall := '0'; -- By default, no need to stall
		should_branch := '0';
		
		-- But in some special cases, we may forward them from the ex/mem registers
		if (ex_mem_is_reg_write = '1') then
			-- if hazard is caused by earlier instr writing to rs
			if (ex_mem_reg_write_index = reg1_read_addr) then
				real_reg1_branch_operand := ex_mem_reg_data;
			end if;
			
			-- if hazard is caused by earlier instr writing to rt
			if (ex_mem_reg_write_index = reg2_read_addr) then
				real_reg2_branch_operand := ex_mem_reg_data; -- if this is a bgez instruction, it isn't too important
			end if;
		end if;
		
		
		-- Set the flag to indicate a successful branch comparison, depending on what type of comparison, we get different constraints
		if (comparison_type = "01") then 
			if (real_reg1_branch_operand = real_reg2_branch_operand) then
				is_branch_successful_comp  := '1';
			else
				is_branch_successful_comp  := '0';
			end if;
		elsif (comparison_type = "10") then -- Only care about MSB of first comparator for BGEZ instructions
			if (real_reg1_branch_operand(31) = '0') then
				is_branch_successful_comp := '1';
			else
				is_branch_successful_comp := '0';
			end if;
		elsif (comparison_type = "11") then
			is_branch_successful_comp := '1';
		else
			is_branch_successful_comp := '0';
		end if;
		
		if (comparison_type /= "00") then
		
			-- Test for stalls required!
			-- Requirements for stalling are more lenient for BGEZ than BEQ, so we have separate forwarding mechanisms for them
			-- We don't want to stall when not really required.
			if (comparison_type = "01") then -- BEQ Statement					
				-- Case 1: Some instruction that modifies at least one of the operands being compared (can be LW, R-type etc)
				if (id_ex_is_reg_write = '1' and (id_ex_reg_write_index = reg1_read_addr or id_ex_reg_write_index = reg2_read_addr)) then
					need_to_stall := '1';
				end if;
					
				-- Case 2: LW instruction exists in the EXEC/DM stage
				if (ex_mem_is_mem_read = '1' and 
					 ex_mem_mem_to_reg = '1' and 
					 ex_mem_is_reg_write = '1' and 
					 (ex_mem_reg_write_index = reg1_read_addr or ex_mem_reg_write_index = reg2_read_addr)) then
						need_to_stall := '1';
				end if;
				
			elsif (comparison_type = "10" or comparison_type = "11") then
				-- Case 1: Some instruction that modifies RS (can be LW, R-type etc)
				if (id_ex_is_reg_write = '1' and (id_ex_reg_write_index = reg1_read_addr)) then
					need_to_stall := '1';
				end if;
					
				-- Case 2: LW instruction exists in the EXEC/DM stage
				if (ex_mem_is_mem_read = '1' and 
					 ex_mem_mem_to_reg = '1' and 
					 ex_mem_is_reg_write = '1' and 
					 (ex_mem_reg_write_index = reg1_read_addr)) then
						need_to_stall := '1';
				end if;
				
			end if;
			
			if (need_to_stall /= '1' and is_branch_successful_comp = '1') then
				-- No stalls required (yet)!
				-- OK, if this is successful, the comparison is successful, and we are certain that the instruction just
				-- before the branch is NOT going to affect our branch decision 
				-- No stalls required, so if we made a match, we can move straightaway!
				
				-- Do the branch!!!
				should_branch := '1';
			end if; 
			
		end if;
		--- 
		is_branch <= should_branch;
		is_branch_stall <= need_to_stall;
		
		
	end process;

end Behavioral;

